////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modif   //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <iostream>
using std::clog;
using std::endl;
//using std::cerr;

#include <algorithm>
using std::min;

#include <cstdlib>

#include <boost/shared_ptr.hpp>
using boost::shared_ptr;

#include "parser.hpp"
#include "messages.hpp"
#include "symboltree.hpp"
#include "options.hpp"

// Visitors to manipulate and output the tree
#include "visitors/declarationvisitor.hpp"
#include "visitors/typevisitor.hpp"
#include "visitors/accesstypevisitor.hpp"
#include "visitors/echovisitor.hpp"
#include "visitors/astvisitor.hpp"
#include "visitors/cppvisitor.hpp"
#include "visitors/sideeffectvisitor.hpp"

#include "assert.hpp"

/******************************************************************************
 * Function implementations                                                   *
 ******************************************************************************/

void report_help_and_version() {
	if (the(Options).version_asked() || the(Options).help_asked()) {
		if (the(Options).version_asked()) {
			clog << "Version 3.1.1 diamond edition" << endl;
			if (the(Options).help_asked())
				clog << endl;
		}
		if (the(Options).help_asked())
			the(Options).report_help(clog);
		exit(0);
	}
}

void generate_output(shared_ptr<Declarations> tree) {
	ostream& out        = the(Options).output();
	unsigned outputType = the(Options).output_type();
	if (outputType & OutputEcho) {
		out << echo(tree, the(Options).output_tab_size());
		if (outputType & (OutputAST | OutputSymbolTable))
			out << string(80, '=') << '\n';
	}
	if (outputType & OutputAST) {
		out << ast(tree);
		if (outputType & OutputSymbolTable)
			out << string(80, '=') << '\n';
	}
	if (outputType & OutputSymbolTable) {
		out << symboltable(tree);
		if (outputType & OutputCPP)
			out << string(80, '=') << '\n';
	}
	if (outputType & OutputCPP)
		out << cppProgram(tree, the(Options).output_tab_size());
}

enum ExtraErrorInfo {
	NoExtraErrorInfo,
	ExtraHelpInfo
};

void check_errors(shared_ptr<Messages> messages, ExtraErrorInfo extraInfo = NoExtraErrorInfo) {
	if (messages->count(Error)) {
		if (the(Options).report_messages()) {
			clog << *messages;
			if (extraInfo == ExtraHelpInfo)
				the(Options).report_help(clog);
		}
		exit(min(messages->count(Error), 255u));
	}
}

/******************************************************************************
 * Main Program                                                               *
 ******************************************************************************/

int main(int argc, char* argv[]) {
	shared_ptr<Messages>      messages(new Messages);
	shared_ptr<UnnamedSymbol> symbolRoot(new UnnamedSymbol);
	shared_ptr<Declarations>  tree;
	
	// Parse the command-line options
	the(Options).parse(argc, argv, messages);
	
	// Abort and report in case of command-line option errors
	check_errors(messages, ExtraHelpInfo);
	
	// If the version or the help-text is asked, print it and abort
	report_help_and_version();
	
	// Parse the code and process the tree. The processing is done such that
	// there is no need to abort the compiler in case of errors, except if
	// there was an unexplained failure in the parser, which throws an exception.
	try {
		tree = parse<Declarations>(the(Options).input(), messages, the(Options).input_file());
	} catch (ParserException e) {
		messages->add(Error, e.message());
		check_errors(messages);
	}
	
	symbolRoot->add_default_symbols();
	
	find_declarations(tree, messages, symbolRoot);
	test_types(tree, messages);
	set_access_types(tree, messages);
	find_side_effects(tree, messages);
	
	check_errors(messages);
	
	// Write the correct output to the correct destination
	generate_output(tree);
	
	// Report the warnings
	assert(messages->count(Error) == 0);
	if (the(Options).report_messages())
		clog << *messages;
	
	return 0;
}
